package com.ittao.taopicture.domain.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ittao.taopicture.domain.user.constant.UserConstant;
import com.ittao.taopicture.domain.user.entity.User;
import com.ittao.taopicture.domain.user.repository.UserRepository;
import com.ittao.taopicture.domain.user.service.UserDomainService;
import com.ittao.taopicture.domain.user.valueobject.UserRoleEnum;
import com.ittao.taopicture.infrastructure.exception.BusinessException;
import com.ittao.taopicture.infrastructure.exception.ErrorCode;
import com.ittao.taopicture.infrastructure.exception.ThrowUtils;
import com.ittao.taopicture.interfaces.dto.VipCode;
import com.ittao.taopicture.interfaces.dto.user.UserAddRequest;
import com.ittao.taopicture.interfaces.dto.user.UserQueryRequest;
import com.ittao.taopicture.interfaces.vo.user.LoginUserVO;
import com.ittao.taopicture.interfaces.vo.user.UserVO;
import com.ittao.taopicture.shared.auth.StpKit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 86134
 * @description 针对表【user(用户)】的数据库操作Service实现
 * @createDate 2025-07-17 21:52:06
 */
@Service
@Slf4j
public class UserDomainServiceImpl implements UserDomainService {

    @Resource
    private UserRepository userRepository;

    /**
     * 用户注册
     *
     * @param userAccount   账号
     * @param userPassword  密码
     * @param checkPassword 校验密码
     * @return
     */
    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword) {
        // 2. 智能处理用户账号重复问题（绕过逻辑删除机制）
        // 使用自定义SQL直接查询，完全绕过MyBatis-Plus的逻辑删除
        User existingUser = userRepository.selectIgnoreLogicDelete(userAccount);

        if (existingUser != null) {
            // 如果用户已被逻辑删除，则恢复该用户账号
            if (existingUser.getIsDelete() == 1) {
                log.info("用户账号 {} 之前被删除，现在恢复注册", userAccount);

                // 3. 密码加密
                String encryptPassword = getEncryptPassword(userPassword);

                // 4. 更新用户信息，相当于重新注册
                existingUser.setUserPassword(encryptPassword);
                existingUser.setUserName(userAccount);
                existingUser.setUserRole(UserRoleEnum.USER.getValue());
                existingUser.setIsDelete(0); // 恢复用户

                // 使用自定义更新方法，绕过逻辑删除机制
                int updateResult = userRepository.updateIgnoreLogicDelete(existingUser);
                if (updateResult == 0) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
                }
                return existingUser.getId();
            } else {
                // 如果用户未被删除，则账号重复
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在");
            }
        }

        // 3. 如果用户不存在，则正常注册
        String encryptPassword = getEncryptPassword(userPassword);

        // 4. 插入数据到数据库中
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(encryptPassword);
        user.setUserName(userAccount);
        user.setUserRole(UserRoleEnum.USER.getValue());
        boolean saveResult = userRepository.save(user);
        if (!saveResult) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
        }
        return user.getId();
    }

    /**
     * 用户登录
     *
     * @param userAccount  账号
     * @param userPassword 密码
     * @param request
     * @return
     */
    @Override
    public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        // 2，对用户传递的密码进行加密
        String encryptPassword = getEncryptPassword(userPassword);
        // 3. 查询数据库中的用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
                .eq("userAccount", userAccount)
                .eq("userPassword", encryptPassword);
        User user = userRepository.getBaseMapper().selectOne(queryWrapper);
        // 不存在，抛异常
        if (user == null) {
            log.info("用户登录失败，账号或密码不正确");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        // 4. 保存用户的登录态
        request.getSession().setAttribute(UserConstant.USER_LOGIN_STATE, user);
        // 使用 Sa-Token 框架管理登录状态，设置过期时间为2小时
        StpKit.SPACE.login(user.getId(), 7200);
        StpKit.SPACE.getSession().set(UserConstant.USER_LOGIN_STATE, user);
        return this.getLoginUserVO(user);
    }

    /**
     * 获取加密后的密码
     *
     * @param userPassword
     * @return
     */
    @Override
    public String getEncryptPassword(String userPassword) {
        // 加盐，混淆密码
        final String SALT = "it_tao";
        return DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
    }

    /**
     * 获取当前登录用户
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        User currentUser = null;

        try {
            // 优先从 Sa-Token 会话中获取用户信息，避免序列化问题
            if (StpKit.SPACE.isLogin()) {
                Object userObj = StpKit.SPACE.getSession().get(UserConstant.USER_LOGIN_STATE);
                if (userObj instanceof User) {
                    currentUser = (User) userObj;
                } else if (userObj instanceof LinkedHashMap) {
                    // 处理 Sa-Token 序列化的情况
                    try {
                        ObjectMapper objectMapper = new ObjectMapper();
                        currentUser = objectMapper.convertValue(userObj, User.class);
                    } catch (Exception e) {
                        log.warn("Sa-Token 会话数据转换失败: {}", e.getMessage());
                    }
                }
            }

            // 如果 Sa-Token 中没有获取到，尝试从 Spring Session 中获取（兼容性处理）
            if (currentUser == null) {
                Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
                if (userObj != null) {
                    if (userObj instanceof User) {
                        currentUser = (User) userObj;
                        // 同步到 Sa-Token 会话中
                        if (StpKit.SPACE.isLogin()) {
                            StpKit.SPACE.getSession().set(UserConstant.USER_LOGIN_STATE, currentUser);
                        }
                    } else if (userObj instanceof LinkedHashMap) {
                        try {
                            ObjectMapper objectMapper = new ObjectMapper();
                            currentUser = objectMapper.convertValue(userObj, User.class);
                            // 同步到 Sa-Token 会话中
                            if (StpKit.SPACE.isLogin()) {
                                StpKit.SPACE.getSession().set(UserConstant.USER_LOGIN_STATE, currentUser);
                            }
                        } catch (Exception e) {
                            log.warn("Spring Session 会话数据转换失败，清除无效会话: {}", e.getMessage());
                            // 清除无效的会话数据
                            request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);
                            if (StpKit.SPACE.isLogin()) {
                                StpKit.SPACE.logout();
                            }
                            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "会话数据异常，请重新登录");
                        }
                    }
                }
            }

        } catch (Exception e) {
            log.error("获取登录用户信息时发生异常: {}", e.getMessage(), e);
            // 清除可能存在问题的会话数据
            try {
                request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);
                if (StpKit.SPACE.isLogin()) {
                    StpKit.SPACE.logout();
                }
            } catch (Exception cleanupException) {
                log.warn("清理会话数据时发生异常: {}", cleanupException.getMessage());
            }
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "获取用户信息失败，请重新登录");
        }

        if (currentUser == null || currentUser.getId() == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        // 重要：每次访问时延长sa-token会话过期时间，与SpringSession保持同步
        Long userId = currentUser.getId();
        if (StpKit.SPACE.isLogin(userId)) {
            // 重新设置sa-token会话的过期时间为7200秒，实现热更新
            StpKit.SPACE.renewTimeout(7200);
        }

        // 从数据库中查询（追求性能的话可以注释，直接返回上述结果）
        long userIdLong = currentUser.getId();
        currentUser = this.getById(userIdLong);
        if (currentUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return currentUser;
    }

    /**
     * 获取登录用户信息
     *
     * @param user
     * @return
     */
    @Override
    public LoginUserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        BeanUtil.copyProperties(user, loginUserVO);
        return loginUserVO;
    }

    /**
     * 获取用户详细信息
     *
     * @param user
     * @return
     */
    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtil.copyProperties(user, userVO);
        return userVO;
    }

    /**
     * 获取用户列表
     *
     * @param userList
     * @return
     */
    @Override
    public List<UserVO> getUserVOList(List<User> userList) {
        if (CollUtil.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream()
                .map(this::getUserVO)
                .collect(Collectors.toList());
    }

    /**
     * 用户退出登录
     *
     * @param request
     * @return
     */
    @Override
    public boolean userLogout(HttpServletRequest request) {
        // 判断是否已经登录
        Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        if (userObj == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "未登录");
        }

        // 获取当前用户ID用于清理sa-token缓存，处理类型转换问题
        User currentUser = null;
        Long userId = null;

        try {
            if (userObj instanceof User) {
                currentUser = (User) userObj;
            } else if (userObj instanceof LinkedHashMap) {
                // 处理JSON序列化导致的LinkedHashMap问题
                ObjectMapper objectMapper = new ObjectMapper();
                currentUser = objectMapper.convertValue(userObj, User.class);
            }

            if (currentUser != null) {
                userId = currentUser.getId();
            }
        } catch (Exception e) {
            log.warn("获取用户ID时发生异常: {}", e.getMessage());
            // 即使获取用户ID失败，也要清理session
        }

        // 移除SpringSession登录态
        request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATE);

        // 重要：清理sa-token的会话数据，这是关键的缓存清理步骤
        try {
            if (userId != null && StpKit.SPACE.isLogin(userId)) {
                // 注销sa-token会话，清理Redis中的satoken缓存
                StpKit.SPACE.logout(userId);
                log.info("用户 {} sa-token会话���存已清理", userId);
            }
        } catch (Exception e) {
            log.warn("清理用户 {} sa-token会话缓存时发生异常: {}", userId, e.getMessage());
            // 不抛出异常，因为SpringSession已经清理，用户仍然可以正常注销
        }

        return true;
    }

    /**
     * 构建查询条件
     *
     * @param userQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();
        String userName = userQueryRequest.getUserName();
        String userAccount = userQueryRequest.getUserAccount();
        String userProfile = userQueryRequest.getUserProfile();
        String userRole = userQueryRequest.getUserRole();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(ObjUtil.isNotNull(id), "id", id);
        queryWrapper.eq(ObjUtil.isNotNull(userRole), "userRole", userRole);
        // 账号为模糊查询
        queryWrapper.like(StringUtils.isNotBlank(userAccount), "userAccount", userAccount);
        // 将用户名为模糊查询
        queryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName);
        queryWrapper.eq(ObjUtil.isNotNull(userProfile), "userProfile", userProfile);
        queryWrapper.orderBy(StringUtils.isNotBlank(sortField), "ascend".equals(sortOrder), sortField);
        return queryWrapper;
    }

    /**
     * 修改用户密码
     *
     * @param oldPassword 旧密码
     * @param newPassword 新密码
     * @param loginUser   当前登录用户
     * @return 是否修改成功
     */
    @Override
    public boolean updateUserPassword(String oldPassword, String newPassword, User loginUser) {
        // 参数校验
        if (loginUser == null || loginUser.getId() == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        // 验证旧密码是否正确
        String encryptOldPassword = getEncryptPassword(oldPassword);
        if (!encryptOldPassword.equals(loginUser.getUserPassword())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "旧密码不正确");
        }

        // 新密码不能与旧密码相同
        if (oldPassword.equals(newPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "新密码不能与旧密码相同");
        }

        // 加密新密码
        String encryptNewPassword = getEncryptPassword(newPassword);

        // 更新数据库中的密码
        User updateUser = new User();
        updateUser.setId(loginUser.getId());
        updateUser.setUserPassword(encryptNewPassword);

        return userRepository.updateById(updateUser);
    }

    /**
     * 兑换会员（会员码兑换）- 添加同步锁防止并发问题
     *
     * @param user
     * @param vipCode
     * @return
     */
    @Override
    public synchronized boolean exchangeVip(User user, String vipCode) {
        // 参数校验
        if (user == null || StrUtil.isBlank(vipCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不能为空");
        }

        // 校验兑换码格式（8位字符）
        if (vipCode.length() != 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "兑换码格式不正确");
        }

        // 检查用户是否已经使用过这个兑换码
        if (vipCode.equals(user.getVipCode())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "兑换码已经使用过，请使用其他兑换码");
        }

        // 检查数据库中是否有其他用户已经使用了这个兑换码
        QueryWrapper<User> vipCodeQueryWrapper = new QueryWrapper<>();
        vipCodeQueryWrapper.eq("vipCode", vipCode);
        User existingUser = userRepository.getOne(vipCodeQueryWrapper);
        if (existingUser != null && !existingUser.getId().equals(user.getId())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "兑换码已被其他用户使用");
        }

        try {
            // 1. 读取JSON配置文件
            ClassPathResource resource = new ClassPathResource("biz/vipCode.json");
            InputStream inputStream = resource.getInputStream();
            String jsonContent = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);

            ObjectMapper objectMapper = new ObjectMapper();
            List<VipCode> vipCodes = objectMapper.readValue(jsonContent, new TypeReference<>() {
            });

            // 2. 判断是否存在某个会员码且未使用
            VipCode targetVipCode = null;
            for (VipCode code : vipCodes) {
                if (vipCode.equals(code.getCode())) {
                    targetVipCode = code;
                    break;
                }
            }

            // 兑换码不存在
            if (targetVipCode == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "兑换码不存在");
            }

            // 兑换码已被其他用户使用
            if (Boolean.TRUE.equals(targetVipCode.getHasUsed())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "兑换��已被使用");
            }

            // 3. 标记兑换码为已使用
            targetVipCode.setHasUsed(true);

            // 将更新后的数据写回JSON文件
            String updatedJson = objectMapper.writeValueAsString(vipCodes);
            java.nio.file.Path jsonPath = java.nio.file.Paths.get("src/main/resources/biz/vipCode.json");
            java.nio.file.Files.write(jsonPath, updatedJson.getBytes(StandardCharsets.UTF_8));

            // 4. 计算VIP过期时间
            Date currentTime = new Date();
            Date vipExpireTime;

            // 如果用户已经是VIP/Admin且还未过期，在现有过期时间基础上延期1年
            Calendar calendar = Calendar.getInstance();
            if ((UserRoleEnum.VIP.getValue().equals(user.getUserRole()) || UserRoleEnum.ADMIN.getValue().equals(user.getUserRole())) &&
                    user.getVipExpireTime() != null &&
                    user.getVipExpireTime().after(currentTime)) {

                calendar.setTime(user.getVipExpireTime());
                calendar.add(Calendar.YEAR, 1);
                vipExpireTime = calendar.getTime();
                log.info("用户 {} VIP会员延期1年，新过期时间：{}", user.getId(), vipExpireTime);
            } else {
                // 如果用户不是VIP/Admin或VIP已过期，从当前时间开始计算1年
                calendar.add(Calendar.YEAR, 1);
                vipExpireTime = calendar.getTime();
                log.info("用户 {} 开通VIP会员1年，过期时间：{}", user.getId(), vipExpireTime);
            }

            // 5. 生成会员编号（如果用户还没有会员编号）
            Long vipNumber = user.getVipNumber();
            if (vipNumber == null) {
                vipNumber = System.currentTimeMillis() * 1000 + user.getId();
            }

            // 6. 更新用户信息
            User updateUser = new User();
            updateUser.setId(user.getId());
            // 如果用户是管理员，保持管理员角色，否则设置为VIP
            if (UserRoleEnum.ADMIN.getValue().equals(user.getUserRole())) {
                updateUser.setUserRole(UserRoleEnum.ADMIN.getValue());
                log.info("管理员用户 {} 使用兑换码延期VIP时间，保持管理员角色", user.getId());
            } else {
                updateUser.setUserRole(UserRoleEnum.VIP.getValue());
            }
            updateUser.setVipExpireTime(vipExpireTime);
            updateUser.setVipCode(vipCode);  // 记录最新使用的兑换码
            updateUser.setVipNumber(vipNumber);

            boolean updateResult = this.updateById(updateUser);
            if (!updateResult) {
                // 如果数据库更新失败，需要回滚JSON文件的更改
                targetVipCode.setHasUsed(false);
                String rollbackJson = objectMapper.writeValueAsString(vipCodes);
                java.nio.file.Files.write(jsonPath, rollbackJson.getBytes(StandardCharsets.UTF_8));
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "VIP开通失败，请重试");
            }

            return true;

        } catch (IOException e) {
            log.error("读取VIP兑换码配置文件失败", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "系统异常，请稍后重试");
        }
    }

    @Override
    public Boolean removeById(Long id) {
        return userRepository.removeById(id);
    }

    @Override
    public boolean updateById(User user) {
        return userRepository.updateById(user);
    }

    @Override
    public User getById(long id) {
        return userRepository.getById(id);
    }

    @Override
    public Page<User> page(Page<User> userPage, QueryWrapper<User> queryWrapper) {
        return userRepository.page(userPage, queryWrapper);
    }

    @Override
    public List<User> listByIds(Set<Long> userIdSet) {
        return userRepository.listByIds(userIdSet);
    }

    @Override
    public long count(QueryWrapper<User> queryWrapper) {
        return userRepository.count(queryWrapper);
    }

    @Override
    public long saveUser(User userEntity, UserAddRequest userAddRequest) {
        // 1. 基础参数校验
        ThrowUtils.throwIf(userAddRequest == null, ErrorCode.PARAMS_ERROR);
        String userAccount = userAddRequest.getUserAccount();

        // 2. 完善账号格式校验（参考register逻辑）
        if (StrUtil.hasBlank(userAccount)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短（至少4位）");
        }

        // 3. 检查账号是否存在（包括逻辑删除的账号，绕过逻辑删除机制）
        User existingUser = userRepository.selectIgnoreLogicDelete(userAccount);

        if (existingUser != null) {
            // 3.1 若账号已被逻辑删除，则恢复该账号
            if (existingUser.getIsDelete() == 1) {
                // 复制请求参数到已有用户对象
                BeanUtil.copyProperties(userAddRequest, existingUser);

                // 设置默认密码（保持原逻辑）
                final String DEFAULT_PASSWORD = "12345678";
                existingUser.setUserPassword(this.getEncryptPassword(DEFAULT_PASSWORD));

                // 恢复账号（取消逻辑删除）
                existingUser.setIsDelete(0);

                // 绕过逻辑删除机制更新
                int updateResult = userRepository.updateIgnoreLogicDelete(existingUser);
                ThrowUtils.throwIf(updateResult == 0, ErrorCode.SYSTEM_ERROR, "恢复用户失败，数据库错误");

                return existingUser.getId();
            } else {
                // 3.2 若账号未删除，则提示已存在
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号已存在");
            }
        }

        // 4. 账号不存在，正常创建新用户
        User user = new User();
        BeanUtil.copyProperties(userAddRequest, user);

        // 设置默认密码（保持原逻辑）
        final String DEFAULT_PASSWORD = "12345678";
        user.setUserPassword(this.getEncryptPassword(DEFAULT_PASSWORD));

        // 确保必要默认值
        if (user.getUserRole() == null) {
            user.setUserRole(UserRoleEnum.USER.getValue());
        }

        boolean saveResult = userRepository.save(user);
        ThrowUtils.throwIf(!saveResult, ErrorCode.OPERATION_ERROR, "创建用户失败，数据库错误");

        return user.getId();
    }
}
